<?php

namespace Yousheng\Ku;

use Yousheng\Ku\Contracts\ResolverInterface;
use Yousheng\Ku\Resolvers\BigintResolver;
use Yousheng\Ku\Resolvers\BinaryResolver;
use Yousheng\Ku\Resolvers\BlobResolver;
use Yousheng\Ku\Resolvers\BoolResolver;
use Yousheng\Ku\Resolvers\CharResolver;
use Yousheng\Ku\Resolvers\DateResolver;
use Yousheng\Ku\Resolvers\DatetimeResolver;
use Yousheng\Ku\Resolvers\DateTimeTzResolver;
use Yousheng\Ku\Resolvers\DecimalResolver;
use Yousheng\Ku\Resolvers\DoubleResolver;
use Yousheng\Ku\Resolvers\EnumResolver;
use Yousheng\Ku\Resolvers\FloatResolver;
use Yousheng\Ku\Resolvers\GeometryResolver;
use Yousheng\Ku\Resolvers\IdResolver;
use Yousheng\Ku\Resolvers\InetResolver;
use Yousheng\Ku\Resolvers\IntegerResolver;
use Yousheng\Ku\Resolvers\IntResolver;
use Yousheng\Ku\Resolvers\JsonbResolver;
use Yousheng\Ku\Resolvers\JsonResolver;
use Yousheng\Ku\Resolvers\LongtextResolver;
use Yousheng\Ku\Resolvers\MacaddressResolver;
use Yousheng\Ku\Resolvers\MediumintResolver;
use Yousheng\Ku\Resolvers\MediumtextResolver;
use Yousheng\Ku\Resolvers\MultilinestringResolver;
use Yousheng\Ku\Resolvers\NumericResolver;
use Yousheng\Ku\Resolvers\RemembertokenResolver;
use Yousheng\Ku\Resolvers\SmallintResolver;
use Yousheng\Ku\Resolvers\TextResolver;
use Yousheng\Ku\Resolvers\TimeResolver;
use Yousheng\Ku\Resolvers\TimestampResolver;
use Yousheng\Ku\Resolvers\TimeTzResolver;
use Yousheng\Ku\Resolvers\TinyintResolver;
use Yousheng\Ku\Resolvers\TinytextResolver;
use Yousheng\Ku\Resolvers\UlidResolver;
use Yousheng\Ku\Resolvers\UuidResolver;
use Yousheng\Ku\Resolvers\VarcharResolver;
use Yousheng\Ku\Resolvers\YearResolver;
use InvalidArgumentException;

/**
 * The resolver to select selector class for the given column.
 */
final class ResolverSelector
{
    /**
     * Select the resolver for the given column.
     *
     * @param  \Yousheng\Ku\Column  $column  The column to select the resolver for
     * @return ResolverInterface The method returns the correct resolver
     *
     * @throws InvalidArgumentException
     *
     * @example ResolverSelector::of(Column::make('id', 'bigint'));
     */
    public static function of(Column $column): ResolverInterface
    {
        // 数值类型、日期和时间类型、字符串类型、二进制类型、JSON 类型和空间类型
        $mapType=[
            "tinyint&bracket == '1'"=>BoolResolver::class,
            "boolean"=>BoolResolver::class,
            "int"=>IntResolver::class,
            "integer"=>IntResolver::class,
            "integer unsigned"=>IntResolver::class,
        ];

    // 主键
        if ($column->field == 'id') {
            return new IdResolver($column);
        }
        if ($column->field == 'remember_token') {
            return new RemembertokenResolver($column);
        }
        // ===========================================================01-数值整型(整数5种、浮点2种、定点1种)
        // 整型1：tinyint,1字节8位， -128~127
        // tinyint unsign ,0~255，
        //          布尔值替代：可以用 0 和 1 分别代表 false 和 true，相较于使用 BOOLEAN 类型，更节省空间
        //          计数场景：当计数范围不超过 255 时，比如统计文章的点赞数、评论数等。
        //          状态标识：用于表示多种状态，例如用户的账户状态（如 0 表示正常，1 表示冻结，2 表示注销等）。
        
        if (($column->type == 'tinyint' && $column->bracket == '1') || $column->type == 'boolean') {
            return new BoolResolver($column);
        }
        if ($column->type == 'tinyint') {
            return new TinyintResolver($column);
        }
        // 整型2：smallint,2字节,16位， -32768~32767,使用 1 位作为符号位（0 表示正数，1 表示负数），剩下 15 位用于表示数值大小
        //       smallint unsign,0~65535
        // ## 使用场景:
        //         年龄存储：人类年龄通常不会超过 200，使用 SMALLINT 可以轻松存储，并且相较于使用更大的整数类型（如 INT），能节省存储空间。
        //         商品数量：在库存管理系统中，如果某种商品的最大库存数量不超过 65535，使用 SMALLINT 就足够了。
        //         分数统计：例如考试分数、游戏得分等，如果分数范围在 SMALLINT 的取值范围内，使用该类型可以高效存储。
        if ($column->type == 'smallint') {
            return new SmallintResolver($column);
        }
        // 整型3：mediumint,3字节,24位， -8388608~8388607, mediumint unsign,0~16777215
        // ## 使用场景:
        //         在一些统计数据、小型计数器或者表示中等规模的数量等场景中
        if ($column->type == 'mediumint') {
            return new MediumintResolver($column);
        }
        // 整型4：int,4字节,32位， -2147483648 到 2147483647, int unsign,0~4294967295(最大42亿多)
        // ## 使用场景:
        //         如用户 ID、订单编号、商品数量等，适合用于计数器、索引列等，能够满足大部分业务场景下的计数需求。
        if ($column->type == 'int') {
            return new IntResolver($column);
        }
        // mysql中 integer 和int是一样的
        if ($column->type == 'integer' || $column->type == 'integer unsigned') {
            return new IntegerResolver($column);
        }
         // 整型5：bigint,5字节,40位， -9223372036854775808 到 9223372036854775807, bigint unsign,0~18446744073709551615(1800多兆)
        // ## 使用场景:
        //         存储时间戳：在某些情况下，可能需要存储精确到毫秒甚至更细粒度的时间戳，这时候 BIGINT 就能派上用场。
        //         存储大的计数器值：例如，网站的访问量计数器、数据库中记录的总行数等。
        //         存储 ID 或序列号：在分布式系统中，可能需要使用大整数作为唯一标识符，以确保全球唯一性。
        // mysql中没有biginteger和bigInteger
        if ($column->type == 'bigint' || $column->type == 'biginteger' || $column->type == 'bigInteger') {
            return new BigintResolver($column);
        }

  
        // 浮点数1
        // 适用于对精度要求不是特别高的场景，例如科学计算中的近似值、图形处理中的坐标值等。在这些场景中，一定程度的精度损失是可以接受的
/*         CREATE TABLE test_float (
            ENT PRIMARY KEY,    ->     id INT AUTO_INCREMENT PRIMARY KEY,
                ->     value_with_precision FLOAT(5)
                -> );
            Query OK, 0 rows affected (0.03 sec)
            
            mysql> INSERT INTO test_float (value_with_precision) VALUES (0.1);
            mysql> INSERT INTO test_float (value_with_precision) VALUES (0.12);
            mysql> INSERT INTO test_float (value_with_precision) VALUES (0.123);
            mysql> INSERT INTO test_float (value_with_precision) VALUES (0.1234);
            mysql> INSERT INTO test_float (value_with_precision) VALUES (0.12345);
            mysql> INSERT INTO test_float (value_with_precision) VALUES (0.123456);
            mysql> INSERT INTO test_float (value_with_precision) VALUES (0.1234567);  // 变成了0.123457
            mysql> INSERT INTO test_float (value_with_precision) VALUES (0.12345678); // 变成了0.123457
            mysql> INSERT INTO test_float (value_with_precision) VALUES (1234.1);
            mysql> INSERT INTO test_float (value_with_precision) VALUES (1234.12);
            mysql> INSERT INTO test_float (value_with_precision) VALUES (1234.123);     // 变成了1234.12
            mysql> INSERT INTO test_float (value_with_precision) VALUES (1234.1234);    // 变成了1234.12
            mysql> INSERT INTO test_float (value_with_precision) VALUES (1234567);      // 变成了1234570
            mysql> INSERT INTO test_float (value_with_precision) VALUES (1234567.1);    // 变成了1234570
            mysql> INSERT INTO test_float (value_with_precision) VALUES (1234567.12);   // 变成了1234570
            mysql> INSERT INTO test_float (value_with_precision) VALUES (1234567.123);  // 变成了1234570
            mysql> INSERT INTO test_float (value_with_precision) VALUES (1234567.1234); // 变成了1234570
            mysql> select * from test_float;

            +----+----------------------+
            | id | value_with_precision |
            +----+----------------------+
            |  8 |                  0.1 |
            |  9 |                 0.12 |
            | 10 |                0.123 |
            | 11 |               0.1234 |
            | 12 |              0.12345 |
            | 13 |             0.123456 |
            | 14 |             0.123457 |
            | 15 |             0.123457 |
            | 16 |               1234.1 |
            | 17 |              1234.12 |
            | 18 |              1234.12 |
            | 19 |              1234.12 |
            | 20 |              1234570 |
            | 21 |              1234570 |
            | 22 |              1234570 |
            | 23 |              1234570 |
            | 24 |              1234570 |
            +----+----------------------+
            */

        // FLOAT(p) 里的 p 代表的是该浮点数的最少有效位数，并非限定小数点前后的位数。
        // p 的取值范围是 0 到 24，当 p 在这个范围时，MySQL 会用 4 个字节来存储 FLOAT 类型的值。
        // float,float(5),float(24)有什么区别
        // float(5) 4个字节储存，可以储存7位数，只有前5位是对的，后面2位是估的


        if ($column->type == 'float' || $column->type == 'real') {
            return new FloatResolver($column);
        }
        // 浮点数2
        // DOUBLE 类型有两种使用方式：占8个字节
        // 简单形式：DOUBLE，直接使用此类型时，它可以存储大约从 -1.7976931348623157E+308 到 -2.2250738585072014E-308、0 以及 2.2250738585072014E-308 到 1.7976931348623157E+308 的数值。
        // 指定精度形式：DOUBLE(M,D)，其中 M 代表总位数（精度），D 代表小数点后的位数（标度）。M 的取值范围是 1 到 255，D 的取值范围是 0 到 30，并且 D 必须小于等于 M。
        if ($column->type == 'double' || $column->type == 'double precision') {
            return new DoubleResolver($column);
        }
 
        // 定点数(精确数值类型) column_name NUMERIC(precision, scale)
        // precision 代表该数值总共可以存储的位数（整数部分和小数部分位数之和），取值范围是 1 到 65
        // scale 表示小数部分的位数，取值范围是 0 到 30，并且不能比 precision 大。
        // 一般适用于需要高精度计算的场景，像财务数据这类。如价格 price NUMERIC(10, 2)
        if ($column->type == 'decimal') {
            return new DecimalResolver($column);
        }
        // decimal与numeric是一样的，写哪个都一样
        if ($column->type == 'numeric') {
            return new NumericResolver($column);
        }

        // =======================================================================================02-日期和时间类型（5种）
        // - DATE：用于存储日期，格式为 YYYY-MM-DD。
        // - TIME：用于存储时间，格式为 HH:MM:SS。
        // - DATETIME：用于存储日期和时间，格式为 YYYY-MM-DD HH:MM:SS。
        // - TIMESTAMP：同样用于存储日期和时间，不过范围比 DATETIME 小，并且会受时区影响。
        // - YEAR：用于存储年份，可以是 2 位或 4 位格式。
        if ($column->type == 'date') {
            return new DateResolver($column);
        }
        if ($column->type == 'time' || $column->type == 'time without time zone') {
            return new TimeResolver($column);
        }
        if ($column->type == 'datetime' || $column->type == 'timestamp without time zone') {
            return new DatetimeResolver($column);
        }
        if ($column->type == 'timestamp') {
            return new TimestampResolver($column);
        }
        if ($column->type == 'year') {
            return new YearResolver($column);
        }
        // mysql中只有上面5种
        // mysql 中没有，PostgreSQL和 Oracle中有
        if ($column->type == 'time with time zone') {
            return new TimeTzResolver($column);
        }
                // mysql 中没有，PostgreSQL和 Oracle中有
                if ($column->type == 'timestamp with time zone') {
                    return new DateTimeTzResolver($column);
                }

        // ==============================================================================03-字符串（5种）
        // - CHAR：固定长度的字符串，最多能存储 255 个字符。当存储的数据长度小于定义的长度时，会用空格填充。
        // - VARCHAR：可变长度的字符串，最多能存储 65,535 个字符。它只会占用实际存储数据所需的空间。
        // - TEXT：用于存储大量文本数据，有 TINYTEXT、TEXT、MEDIUMTEXT 和 LONGTEXT 几种类型，存储容量逐渐增大。
        // - ENUM：从预定义的字符串列表里选择一个值进行存储。
        // - SET：从预定义的字符串列表里选择零个或多个值进行存储。

        if ($column->type == 'char') {
            return new CharResolver($column);
        }
        if ($column->type == 'char' && $column->bracket == '26') {
            return new UlidResolver($column);
        }
        if (($column->type == 'char' && $column->bracket == '36') || $column->type == 'uuid') {
            return new UuidResolver($column);
        }

        if (($column->type == 'varchar' && $column->bracket == '45') || $column->type == 'inet') {
            return new InetResolver($column);
        }
        if (($column->type == 'varchar' && $column->bracket == '17') || $column->type == 'macaddr') {
            return new MacaddressResolver($column);
        }
        
        if ($column->type == 'varchar' || $column->type == 'character varying' || $column->type == 'character') {
            return new VarcharResolver($column);
        }
        if ($column->type == 'tinytext') {
            return new TinytextResolver($column);
        }
        if ($column->type == 'text') {
            return new TextResolver($column);
        }
        if ($column->type == 'mediumtext') {
            return new MediumtextResolver($column);
        }
        if ($column->type == 'longtext') {
            return new LongtextResolver($column);
        }

        if ($column->type == 'enum') {
            return new EnumResolver($column);
        }
        // set

        // ==============================================================================04 二进制(3种)
        // - BINARY：固定长度的二进制字符串，最多能存储 255 个字节。
        // - VARBINARY：可变长度的二进制字符串，最多能存储 65,535 个字节。
        // - BLOB：用于存储大量二进制数据，有 TINYBLOB、BLOB、MEDIUMBLOB 和 LONGBLOB 几种类型，存储容量逐渐增大。
        if ($column->type == 'binary' || $column->type == 'bytea') {
            return new BinaryResolver($column);
        }

        if ($column->type == 'blob') {
            return new BlobResolver($column);
        }
        // ==============================================================================05 Json类型(3种)
        if ($column->type == 'json') {
            return new JsonResolver($column);
        }

        // PostgreSQL 中使用jsonb储存json数据
        if ($column->type == 'jsonb') {
            return new JsonbResolver($column);
        }
        // ==============================================================================06 空间类型(4种)
        // - GEOMETRY：可存储任何类型的几何对象。
        // - POINT：用于存储点的坐标。
        // - LINESTRING：用于存储线的信息。
        // - POLYGON：用于存储多边形的信息。

        if ($column->type == 'geometry' || $column->type == 'user-defined') {
            return new GeometryResolver($column);
        }

        if ($column->type == 'multilinestring') {
            return new MultilinestringResolver($column);
        }

        throw new InvalidArgumentException($column.' This column cannot be resolved.');
    }
}
